package cz.cuni.mff.ufal.volk.command;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import cz.cuni.mff.ufal.volk.message.ChoreographyMessage;
import cz.cuni.mff.ufal.volk.message.IdMessage;
import cz.cuni.mff.ufal.volk.message.Message;
import cz.cuni.mff.ufal.volk.message.PlaySoundMessage;
import cz.cuni.mff.ufal.volk.message.PlayStreamMessage;
import cz.cuni.mff.ufal.volk.message.SetLedColorMessage;
import cz.cuni.mff.ufal.volk.message.SetPalletteMessage;
import cz.cuni.mff.ufal.volk.message.WaitMessage;
import cz.cuni.mff.ufal.volk.nabaztag.ChoreographyPallette;
import cz.cuni.mff.ufal.volk.nabaztag.Led;

public class MessageCommand extends Command {

	private static Random rand = new Random();

  public MessageCommand() {
  	this.setId(rand.nextLong());
  	//this.setId(1);
  	this.waitForPreviousCommand();
  }

  public MessageCommand(Message... messages) {
    for (Message msg : messages) {
      this.messages.add(msg);
    }
  }

  @Override
  public byte commandType() {
    return 0xA;
  }

  @Override
  public byte[] getBytes() {
    StringBuilder commandBody = new StringBuilder();
    for (Message msg : messages) {
      String cmd = msg.getCommand();
      String params = msg.getParameterString();
      cmd.getClass(); // null check
      if (params == null || params.equals("")) {
        commandBody.append(String.format("%s\n", cmd));
      } else {
        commandBody.append(String.format("%s %s\n", cmd, params));
      }
    }
    byte[] bytes = commandBody.toString().getBytes();
    encode(bytes, 0, bytes.length);
    return bytes;
  }

  private final List<Message> messages = new ArrayList<Message>();

  public void addMessage(Message msg) {
    messages.add(msg);
  }

  public final void setId(long id) {
  	// must be final (is called in constructor)
    addMessage(new IdMessage(id));
  }

  public void setLedColor(Led led, int red, int green, int blue) {
    addMessage(new SetLedColorMessage(led, red, green, blue));
  }

  public void playChoreography(String url) {
    addMessage(new ChoreographyMessage(url));
  }

  public void playSound(String url) {
    addMessage(new PlaySoundMessage(url));
  }

  public void playStream(String url) {
    addMessage(new PlayStreamMessage(url));
  }

  public void setChoreographyPallette(ChoreographyPallette pallette) {
    addMessage(new SetPalletteMessage(pallette));
  }

  public void waitForPreviousCommand() {
    addMessage(new WaitMessage());
  }

  private final static int[] INVTABLE = new int[] { 1, 171, 205, 183, 57, 163, 197, 239, 241, 27, 61, 167, 41, 19, 53, 223, 225, 139,
    173, 151, 25, 131, 165, 207, 209, 251, 29, 135, 9, 243, 21, 191, 193, 107, 141, 119, 249, 99, 133, 175, 177, 219, 253, 103,
    233, 211, 245, 159, 161, 75, 109, 87, 217, 67, 101, 143, 145, 187, 221, 71, 201, 179, 213, 127, 129, 43, 77, 55, 185, 35, 69,
    111, 113, 155, 189, 39, 169, 147, 181, 95, 97, 11, 45, 23, 153, 3, 37, 79, 81, 123, 157, 7, 137, 115, 149, 63, 65, 235, 13,
    247, 121, 227, 5, 47, 49, 91, 125, 231, 105, 83, 117, 31, 33, 203, 237, 215, 89, 195, 229, 15, 17, 59, 93, 199, 73, 51, 85, 255 };

  /**
   * Encodes the command body using the Nabaztag obfuscating algorithm.
   *
   * @param bytes byte array to be encoded
   * @param offset index of the first byte to be encoded; if offset is not 0 it is assumed that all
   *        previous bytes have already been encoded
   * @param length number of bytes to be encoded
   */
  private static void encode(byte[] bytes, int offset, int length) {
    if (length < 1) {
      return;
    }

    byte previous = offset == 0 ? 35 : bytes[offset - 1];

    for (int i = offset; i < offset + length; i++) {
      byte current = bytes[i];
      bytes[i] = (byte) ((INVTABLE[previous % 128] * current + 47) % 256);
      previous = current;
    }
  }

}
